class CNNLSTM(nn.Module):
    def __init__(self):
        super(CNNLSTM， self).__init__()
        # 卷积+BN+ReLU [None，1，10，12]==>[None，8，10，12]
        self.conv1 = nn.Conv2d(in_channels=1，
                                   out_channels=8，
                                   kernel_size=3，
                                   stride=1，
                                   padding=1，
                                   dilation=1， #空洞参数为1，标准卷积，利用添加空洞扩大感受野,利用添加空洞扩大感受野。                                   
                                   groups=1， # 分组卷积 如果group是2，对应要将输入的in_channels通道
                                               # 分成2个in_channels/2的通道，参数量下降1倍
                                  bias=False)
        self.bn = nn.BatchNorm2d(num_features=8) #参看2.2节 “BN批归一化/标准化详解” 
        # num_features:输入通道C，即：即特征的数量，输入参数为:(B*C*H*W)
        # self.relu = nn.ReLU()
        # 池化下采样 [None，8，10，12]==>[None，8，10，6]
        self.pool = nn.MaxPool2d(kernel_size=(1，2)，stride=None)
        # stride:默认步长跟最大池化窗口大小一致， 
        # 窗口每次向右滑动2个元素，向下滑动1个元素位置

        # 1*1卷积调整通道数 [None，8，10，6]==>[None，1，10，6]
        self.conv2 = nn.Conv2d(in_channels=8，
                                    out_channels=1，
                                    kernel_size=3，
                                    padding=1，
                                    bias=False)
        self.lstm = nn.LSTM(input_size=6，
                                hidden_size=32，  # 这里隐藏单元为32，
                                # 用16个 Dropout(p=0.2)效果不理想，
                                # 原因可以参看前面dropout章节
                                num_layers=1， 
                                batch_first=True，# (batch_size，seq_len，hidden_size) 
                                bidirectional=False)

        self.dropout = nn.Dropout(p=0.5) 
       # 随机删除p=50%神经元(归零的概率)，防止过拟合

        self.fc = nn.Linear(in_features=32， out_features=1)
    def forward(self， inputs):
        # 调整维度 [None，10，12]==>[None，10，12，1]==>[None，1，10，12]
        x = torch.reshape(inputs，(-1，inputs.shape[1]，inputs.shape[2]， 1)) 
        x = x.permute(0， 3， 1 ， 2)  #[None，1，10，12]

        # 卷积+BN+ReLU  [None，1，10，12]==>[None，8，10，12]
        x = self.conv1(x) # x:(B，C，H，W)
        x = self.bn(x) # 批标准化
        x = F.relu(x)  # relu激活函数
        # 池化下采样 [None，8，10，12]==>[None，8，10，6]
        x =  self.pool(x)
        # 1*1卷积调整通道数 [None，8，10，6]==>[None，1，10，6]
        x = self.conv2(x)

        # 把 1的维度挤压掉 [None，1，10，6]==>[None，10，6]
        x = torch.squeeze(x)  #如果不指定维度dim，删除所有维度为1的维度

        # [None，10，6] ==> [None，10，16] 
        h_0 = torch.randn(1， x.size(0)， 32) 
        #(num_layers，batch_size，hidden_size)
        c_0 = torch.randn(1， x.size(0)， 32)
        # output(batch_size， seq_len， num_directions * hidden_size)

        x， (h_n， c_n)=self.lstm(x， (h_0， c_0)) # 未提供（h_0，c_0）则为零
        # x输入:[None，10，6] => x输出:[None，10，32]   
        # h_n， c_n:[1，batch_size，16]
        # 由于nn.LSTM中 batch_first=True， 
        # x输入:(batch_size，seq_len，，hidden_size)
                
        x = F.relu(x)  # relu激活函数 或 self.relu(x)
        x = self.dropout(x) # 随机杀死20%神经元防止过拟合

        # 输入x：[None，10，32]==>输出pred：[None，10，1]
        pred = self.fc(x) 
        # pred: [batch，10，1] (batch_size， seq_len，out_features )
        pred = pred[:， -1， :] #(batch，1) 返回seq_len最后一个输出
        return pred

